#include "mvc/view/opengl_widget.h"
#include "common/texture_indexs.h"

#include <iostream>
#include <QVector3D>
#include <QMouseEvent>
#include <QPoint>

//#define GLUT_DISABLE_ATEXIT_HACK
#include <GL/glu.h>
#include <GL/glut.h>

struct
{
    QPoint position;
    Qt::MouseButton button_pressed;
} _mouse;

Opengl_widget::Opengl_widget(QWidget *parent) :
    QGLWidget(QGLFormat(), parent), _config(0),
    camera(QVector3D(0.0, 0.0, -50.0), 120.0, 0.0, 145.0)
{
    //cout << "TODO: Contructor GL" << endl;

   uavtex_enabled = true;
   simtex_enabled = false;
   simulate = false;
}

/**
  *
  */
Opengl_widget::~Opengl_widget()
{
    for(int i = 0; i < _textures.size(); i++)
        if(_textures.at(i))
            delete _textures.at(i);
}

/**
  * Actions to draw a configuration
  */
 void Opengl_widget::draw_config(const Configuration *config, bool draw_sim, int step)
 {
    _config = config;
    simulate = draw_sim;
    cs_step = step;
    updateGL();
 }

 /**
   *
   */
 int Opengl_widget::add_texture(ITexture_image *texture)
 {
    _textures.push_back(texture);

    return (_textures.size() - 1);
 }

 //
 // PROTECTED METHODS
 //

/**
  *
  */
void Opengl_widget::initializeGL()
{
//    cout << "InitializeGL" << endl;
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH);
    glShadeModel(GL_FLAT);
    glEnable(GL_DEPTH_TEST);

    for(int i = 0; i < _textures.size(); i++)
        if(_textures.at(i))
            enable_texture(i);

    camera.locate_camera();
}

/**
  *
  */
void Opengl_widget::resizeGL(int width, int height)
{
    glViewport(0, 0, width, height);
}

/**
  *
  */
void Opengl_widget::paintGL()
{
//    cout << "paintGL" << endl;
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glClearColor(1.0, 1.0, 1.0, 1.0);

    if(_config)
    {
        if(uavtex_enabled || simtex_enabled)
        {
            glEnable(GL_TEXTURE_2D);
            glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);

            glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
            glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
        }

        glColor3f(0.0, 0.0, 0.0);

        _config->draw(uavtex_enabled);

        if(uavtex_enabled && not simtex_enabled)
            glDisable(GL_TEXTURE_2D);

        if(simulate)
            _config->draw_simulation(cs_step, simtex_enabled);

        if(simtex_enabled)
            glDisable(GL_TEXTURE_2D);
    }
}

/**
  * the mouse event listener
  */
void Opengl_widget::mouseMoveEvent(QMouseEvent *event)
{
    double diff_x;
    double diff_y;
    char direction;

    if(_mouse.position.x() < event->x())
    {
        direction = 'P';
        diff_x = event->x() - _mouse.position.x();
        diff_y = event->y() - _mouse.position.y();
    }
    else
    {
        direction = 'N';
        diff_x = _mouse.position.x() - event->x();
        diff_y = _mouse.position.y() - event->y();
    }

    if(_mouse.button_pressed == Qt::LeftButton)
    {
        if(direction == 'P')
        {
            camera.rotate_camera(ROTATE_L, diff_x);
            camera.rotate_camera(ROTATE_U, diff_y);
        }
        else if(direction == 'N')
        {
            camera.rotate_camera(ROTATE_R, diff_x);
            camera.rotate_camera(ROTATE_D, diff_y);
        }
    }
    else if(_mouse.button_pressed == Qt::RightButton)
    {
        if(direction == 'P')
        {
            camera.move_camera(MOVE_R, diff_x);
            camera.move_camera(MOVE_D, diff_y);
        }
        else if(direction == 'N')
        {
            camera.move_camera(MOVE_L, diff_x);
            camera.move_camera(MOVE_U, diff_y);
        }
    }
    else if(_mouse.button_pressed == Qt::MiddleButton)
    {
        if(direction == 'P')
            camera.zoom(DECZ, diff_y);
        else if(direction == 'N')
            camera.zoom(INCZ, diff_y);
    }

    _mouse.position = event->pos();
    updateGL();
}

/**
  *
  */
void Opengl_widget::mousePressEvent(QMouseEvent *event)
{
    _mouse.position = event->pos();
    _mouse.button_pressed = event->button();
}

/**
  *
  */
void Opengl_widget::mouseReleaseEvent(QMouseEvent *event)
{
//    cout << "TODO: Opengl_widget.MouseReleaseEvent" << endl;
}

//
// PRIVATE METHODS
//

/**
  * Enabled texture
  */
void Opengl_widget::enable_texture(int index)
{
//    cout << "Enable: " << index << endl;

    if(not _textures.at(index)->is_created())
        _textures.at(index)->make_texture();

    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

    if(not glIsTexture(get_texture_name(index)))
    {
        GLuint tex_name;
        glGenTextures(1, &tex_name);
        add_texture_name(index, tex_name);
    }

    glBindTexture(GL_TEXTURE_2D, get_texture_name((index)));
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);

    int level = 0;
    glTexImage2D(GL_TEXTURE_2D, level++, GL_RGBA, 32, 32, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                 _textures.at(index)->get_texture(32));
    glTexImage2D(GL_TEXTURE_2D, level++, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                 _textures.at(index)->get_texture(16));
    glTexImage2D(GL_TEXTURE_2D, level++, GL_RGBA, 8, 8, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                 _textures.at(index)->get_texture(8));
    glTexImage2D(GL_TEXTURE_2D, level++, GL_RGBA, 4, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                 _textures.at(index)->get_texture(4));
    glTexImage2D(GL_TEXTURE_2D, level++, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                 _textures.at(index)->get_texture(2));
    glTexImage2D(GL_TEXTURE_2D, level++, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                 _textures.at(index)->get_texture(1));
}
